using System;
using System.Data;
using System.Diagnostics;
using System.IO;
using Microsoft.VisualStudio.DebuggerVisualizers;

namespace DbVisualizer.DbConnection
{
    internal class DebugeeObjectSource : VisualizerObjectSource
    {
        private string sql;

        private readonly IDataTableFactory dataTableFactory;

        public DebugeeObjectSource()
            : this(new DataTableFactory())
        {
        }

        private DebugeeObjectSource(IDataTableFactory dataTableFactory)
        {
            this.dataTableFactory = dataTableFactory;
        }

        private DataTable ExecuteSql(System.Data.Common.DbConnection connection, string sql)
        {
            DataTable resultTable;

            using (var command = connection.CreateCommand())
            {
                command.CommandText = sql;

                using (var reader = command.ExecuteReader())
                {
                    resultTable = dataTableFactory.Create(reader);
                }
            }

            return resultTable;
        }

        public override void TransferData(object target, Stream incomingData, Stream outgoingData)
        {
            sql = GetStringFromStream(incomingData);
        }

        public override void GetData(object target, Stream outgoingData)
        {
            var result = new QueryResult();

            try
            {
                if (!string.IsNullOrEmpty(sql))
                {
                    try
                    {
                        var sw = Stopwatch.StartNew();

                        var resultTable = ExecuteSql((System.Data.Common.DbConnection)target, sql);
                        sw.Stop();

                        result.ResultTable = resultTable;
                        result.QueryTime = sw.Elapsed;
                    }
                    catch (Exception ex)
                    {
                        result.Exception = ex;
                    }

                    base.GetData(result, outgoingData);
                }
                else
                {
                    base.GetData(((System.Data.Common.DbConnection)target).ConnectionString, outgoingData);
                }
            }
            finally
            {
                sql = null;
            }
        }

        private static string GetStringFromStream(Stream stream)
        {
            using (var reader = new StreamReader(stream))
            {
                return reader.ReadToEnd();
            }
        }
    }
}